﻿using System;
using System.Linq;
using System.Linq.Expressions;
using System.Reflection;
using CoolCode.Linq;

namespace CoolCode.Data.Xml
{
    /// <summary>
    /// implementation of IQueryProvider, part of the LINQ Provider API
    /// </summary>
    class XmlQueryProvider : IQueryProvider
    {
        /// <summary>
        /// refers to XmlContext that calling code instantiated
        /// </summary>
        public IQueryExecutor Context { get; set; }

        /// <summary>
        /// Non-generic version, returns current query to 
        /// calling code as its constructing the query
        /// </summary>
        /// <param name="expression">Expression tree</param>
        /// <returns>IQueryable that can be executed</returns>
        public IQueryable CreateQuery(Expression expression)
        {
            Type elementType =  expression.Type.GetElementTypeOrSelf();
            try
            {
                return (IQueryable)Activator.CreateInstance(
                    typeof(XmlQueryable<>) .MakeGenericType(elementType),
                    new object[] { this, expression });
            }
            catch (TargetInvocationException tie)
            {
                throw tie.InnerException;
            }
        }

        /// <summary>
        /// generic version, returns current query to 
        /// calling code as its constructing the query
        /// </summary>
        /// <typeparam name="TResult">current object type being worked with</typeparam>
        /// <param name="expression">expression tree for query</param>
        /// <returns>IQueryable that can be executed</returns>
        public IQueryable<TResult> CreateQuery<TResult>(Expression expression) 
        {
            //return new XmlQueryable<TResult>(this, expression);
            return (IQueryable<TResult>)Activator.CreateInstance(
                    typeof(XmlQueryable<>).MakeGenericType(typeof(TResult)),
                    new object[] { this, expression });
        }

        /// <summary>
        /// non-generic execute, delegates execution to XmlContext
        /// </summary>
        /// <param name="expression">Expression Tree</param>
        /// <returns>list of results from query</returns>
        public object Execute(Expression expression)
        {
            return Context.Execute(expression);
        }

        /// <summary>
        /// generic execute, delegates execution to XmlContext
        /// </summary>
        /// <typeparam name="TResult">type of query</typeparam>
        /// <param name="expression">Expression tree</param>
        /// <returns>list of results from query</returns>
        public TResult Execute<TResult>(Expression expression)
        {
            bool isEnumerable = (typeof(TResult).Name == "IEnumerable`1");
            if (!isEnumerable)
            {
                var query = Context.Execute(expression);
                var result = query as IQueryable<object>;
                if (result != null)
                {
                    return  (TResult) result.FirstOrDefault();
                }
                return default(TResult);
            }
            return (TResult)Context.Execute(expression);
        }

        /// <summary>
        /// generic execute, delegates execution to XmlContext
        /// </summary>
        /// <typeparam name="TResult">type of query</typeparam>
        /// <param name="expression">Expression tree</param>
        /// <returns>list of results from query</returns>
        public System.Collections.Generic.IEnumerable<T> Execute<T>(Expression expression, bool isEnumerable) where T:class 
        {
            //bool isEnumerable = (typeof(TResult).Name == "IEnumerable`1");

            return Context.Execute<T>(expression);
        }


    }
}
